package com.jreque.client;

import com.jreque.persistance.IQueuePersistance;
import com.jreque.persistance.QueuePersistanceFactory;
import com.jreque.queue.ITaskInvocation;
import com.jreque.queue.QueueManager;
import com.jreque.queue.Task;
import com.jreque.queue.TaskInvocationByName;
import com.jreque.queue.TaskInvocationByObjectMethod;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.io.Reader;
import java.lang.*;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Properties;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 * Entry point class for running console Queue's client.
 */
public class ConsoleClient {
    
    private QueueManager manager;
    private BufferedReader input;
    private PrintStream output;
    
    /**
     */
    public static void main(java.lang.String[] args) {
        if(args.length < 1) {
            System.err.println("Property file name not specified");
            System.exit(1);
        }
        String propFileName = args[0];
        File propFile = new File(propFileName);
        if(!propFile.exists()) {
            System.err.println("File: " + propFileName + " not found");
            System.exit(1);
        }
        if(!propFile.canRead()) {
            System.err.println("Could not read file: " + propFileName);
            System.exit(1);
        }
        FileInputStream fis = null;
        try {
            fis = new FileInputStream(propFile);
        } catch (FileNotFoundException ex) {}
        Properties p = new Properties();
        try {
            p.load(fis);
        } catch (IOException ex) {
            System.err.println("Error reading file: " + propFileName);
            System.exit(1);
        }
        IQueuePersistance persistance = null;
        try {
            persistance = QueuePersistanceFactory.getInstanceForProperties(p);
        } catch (ClassNotFoundException ex) {
            System.err.println("Wrong persistance class specified");
            System.err.println(ex.getMessage());
            System.exit(1);
        } catch (Exception ex) {
            System.err.println("Error initializing the persistance layer");
            System.exit(1);
        }
        QueueManager manager = new QueueManager(persistance);
        
        BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
        
        ConsoleClient cc = new ConsoleClient(manager, reader, System.out);
        cc.commandLoop();
        
    }

    public ConsoleClient(QueueManager manager, BufferedReader input, PrintStream output) {
        if(manager == null) throw new RuntimeException("Queue manager unspecified");
        if(input == null) throw new RuntimeException("Input reader unspecified");
        this.manager = manager;      
        this.input = input;
        this.output = output;
    }
    
    

    /**
     */
    public void commandLoop() {
        while (true) {
            runCommand(getCommand());
        }
    }

    /**
     */
    public void enqueueTaskCommand() {
        String queueName = null;
        while(queueName == null || queueName.isEmpty()) {
            queueName = readLn("On what queue do you want to enqueue the task?");
        }
        
            SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-DD HH:mm:ss");
        Date startDate = null;
        while(true) {
            String dateStr = readLn("When to start the task(YYYY-MM-DD HH:mm:ss or type \"immediate\")?");
            if("immediate".equals(dateStr)) {
                break;
            }
            try {
                startDate = format.parse(dateStr);
                break;
            } catch (ParseException e) {}
            printLn("Invalid input");
        }
        
        int optionChosen = -1;
        while(optionChosen < 0) {
            String input = readLn("Do you want to enqueue a named task(1) or specify it yourself(2)?");
            try {
                int i = Integer.parseInt(input);
                if(i > 0 && i < 3) {
                    optionChosen = i;
                }
            } catch (NumberFormatException e) {
                printLn("The input value must be 1 or 2.");
            }
        }
        
        ITaskInvocation invocation = null;
        if(optionChosen == 1) {
            while(true) {
                String execName = readLn("Enter task name:");
                if(execName == null || execName.isEmpty()) continue;
                invocation = new TaskInvocationByName(execName);
                break;
            }
        } else {
            String object = null;
            String method = null;
            while(object == null || object.isEmpty()) {
                object = readLn("Class name:");
            }
            while(method == null || method.isEmpty()) {
                method = readLn("Method name");
            }
            invocation = new TaskInvocationByObjectMethod(object, method);
        }
        Task t = new Task(queueName, invocation, null, startDate);
        int qId = manager.enqueueTask(t);
        printLn("Enqueued task with ID:" + qId);
    }

    /**
     */
    public void dequeueTaskCommand() {
        int id = -1;
        while(id < 0) {
            String idStr = readLn("Task ID:");
            try {
                id = Integer.parseInt(idStr);
            } catch (NumberFormatException e) {}
        }
        if(manager.deleteTask(id)) {
            printLn("Task deleted");
        } else {
            printLn("Operation failed");
        }
    }

    /**
     */
    public void getStatsCommand() {
        String queueName = null;
        while(queueName == null || queueName.isEmpty()) {
            queueName = readLn("Print stats for what queue?");
        }
        HashMap hm = manager.getStats(queueName);
        if(hm == null || hm.isEmpty()) {
            printLn("No stats for queue:" + queueName);
        }
        printLn("STATS:");
        for(Object o : hm.keySet()) {
            printLn(o.toString() + ": " + String.valueOf(hm.get(o)));
        }
    }

    /**
     * @return 
     */
    public String getCommand() {
        while(true) {
            String cmd = readLn("Enter command:");
            if("dequeue".equals(cmd)
                    || "enqueue".equals(cmd)
                    || "stats".equals(cmd)
                    || "quit".equals(cmd)) {
                return cmd;
            }
            printLn("Unrecognized command:" + cmd);
        }
    }

    /**
     * @param command 
     */
    public void runCommand(String command) {
        if("enqueue".equals(command)) {
            enqueueTaskCommand();
        } else if ("dequeue".equals(command)) {
            dequeueTaskCommand();
        } else if ("stats".equals(command)) {
            getStatsCommand();
        } else if ("quit".equals(command)) {
            System.exit(0);
        }
    }
    
    /**
     * Prints the supplied text to the output stream
     * @param text 
     */
    private void printLn(String text) {
        if(output != null) {
            output.println(text);
        }
    }
    
    /**
     * Reads a line from the inut stream
     * @return the text read
     */
    private String readLn() {
        try {
            return input.readLine();
        } catch (IOException ex) {
            return null;
        }
    }
    
    /**
     * Prints the supplied text and reads a line
     * @return the text read
     */
    private String readLn(String text) {
        printLn(text);
        return readLn();
    }
}

